import com.sun.javafx.geom.Edge;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.*;

public class homework_09 extends Application {

    private static final int X_MIN = 100;
    private static final int X_MAX = 300;
    private static final int Y_MIN = 100;
    private static final int Y_MAX = 300;

    private static final int INSIDE = 0;
    private static final int LEFT = 1;
    private static final int RIGHT = 2;
    private static final int BOTTOM = 4;
    private static final int TOP = 8;

    @Override
    public void start(Stage primaryStage) {
        Canvas canvas = new Canvas(400, 400);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        // 绘制红色虚线边框
        drawDashedRectangle(gc, X_MIN, Y_MIN, X_MAX - X_MIN, Y_MAX - Y_MIN, Color.RED);

        // 生成艺术图形（旋转八边形）
        List<int[]> shapeEdges = createRotatedOctagon(200, 200, 120, 22.5);

        // 绘制原始图形（蓝色）
        gc.setStroke(Color.BLUE);
        shapeEdges.forEach(edge -> drawLine(gc, edge[0], edge[1], edge[2], edge[3], Color.BLUE));

        // 裁剪并收集裁剪后的线段
        List<int[]> clippedEdges = new ArrayList<>();
        for (int[] edge : shapeEdges) {
            int[] clipped = cohenSutherlandClip(edge[0], edge[1], edge[2], edge[3]);
            if (clipped != null) {
                clippedEdges.add(clipped);
                drawLine(gc, clipped[0], clipped[1], clipped[2], clipped[3], Color.GOLD);
            }
        }

        // 构建填充多边形
        if (!clippedEdges.isEmpty()) {
            List<Point> polygon = new ArrayList<>();
            for (int[] edge : clippedEdges) {
                polygon.add(new Point(edge[0], edge[1]));
                polygon.add(new Point(edge[2], edge[3]));
            }
            scanlineFill(gc, polygon, Color.DARKVIOLET);
        }

        StackPane root = new StackPane(canvas);
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Artistic Clipping with Fill");
        primaryStage.show();
    }

    // 创建旋转八边形（参数：旋转角度）
    private List<int[]> createRotatedOctagon(int centerX, int centerY, int size, double rotateDeg) {
        List<int[]> edges = new ArrayList<>();
        double rotateRad = Math.toRadians(rotateDeg);
        int[][] points = new int[8][2];

        // 生成八个顶点
        for (int i = 0; i < 8; i++) {
            double angle = Math.toRadians(45 * i) + rotateRad;
            int x = (int) (centerX + size * Math.cos(angle));
            int y = (int) (centerY + size * Math.sin(angle));
            points[i][0] = x;
            points[i][1] = y;
        }

        // 连接顶点形成八边形
        for (int i = 0; i < 8; i++) {
            int next = (i + 1) % 8;
            edges.add(new int[]{points[i][0], points[i][1], points[next][0], points[next][1]});
        }

        return edges;
    }

    // 完整Cohen-Sutherland算法实现
    private int[] cohenSutherlandClip(int x1, int y1, int x2, int y2) {
        int code1 = computeRegionCode(x1, y1);
        int code2 = computeRegionCode(x2, y2);

        while (true) {
            if ((code1 | code2) == 0) {
                return new int[]{x1, y1, x2, y2};
            } else if ((code1 & code2) != 0) {
                return null;
            } else {
                int x = 0, y = 0;
                int codeOut = code1 != 0 ? code1 : code2;

                if ((codeOut & TOP) != 0) {
                    x = x1 + (x2 - x1) * (Y_MAX - y1) / (y2 - y1);
                    y = Y_MAX;
                } else if ((codeOut & BOTTOM) != 0) {
                    x = x1 + (x2 - x1) * (Y_MIN - y1) / (y2 - y1);
                    y = Y_MIN;
                } else if ((codeOut & RIGHT) != 0) {
                    y = y1 + (y2 - y1) * (X_MAX - x1) / (x2 - x1);
                    x = X_MAX;
                } else if ((codeOut & LEFT) != 0) {
                    y = y1 + (y2 - y1) * (X_MIN - x1) / (x2 - x1);
                    x = X_MIN;
                }

                if (codeOut == code1) {
                    x1 = x;
                    y1 = y;
                    code1 = computeRegionCode(x1, y1);
                } else {
                    x2 = x;
                    y2 = y;
                    code2 = computeRegionCode(x2, y2);
                }
            }
        }
    }

    // 区域码计算
    private int computeRegionCode(int x, int y) {
        int code = INSIDE;
        if (x < X_MIN) code |= LEFT;
        else if (x > X_MAX) code |= RIGHT;
        if (y < Y_MIN) code |= BOTTOM;
        else if (y > Y_MAX) code |= TOP;
        return code;
    }

    // 扫描线填充算法（完整实现）
    private void scanlineFill(GraphicsContext gc, List<Point> polygon, Color color) {
        if (polygon.size() < 3) return;

        // 创建边表
        List<Edge> edges = new ArrayList<>();
        for (int i = 0; i < polygon.size(); i++) {
            Point p1 = polygon.get(i);
            Point p2 = polygon.get((i + 1) % polygon.size());
            if (p1.y != p2.y) {
                edges.add(new Edge(p1.x, p1.y, p2.x, p2.y));
            }
        }

        // 确定有效扫描范围
        int yMin = Math.max(edges.stream().mapToInt(e -> e.yMin).min().orElse(Y_MIN), Y_MIN);
        int yMax = Math.min(edges.stream().mapToInt(e -> e.yMax).max().orElse(Y_MAX), Y_MAX);

        gc.setStroke(color);
        for (int y = yMin; y <= yMax; y++) {
            List<Integer> intersections = new ArrayList<>();

            // 计算有效交点
            for (Edge edge : edges) {
                if (edge.yMin <= y && y <= edge.yMax) {
                    int x = (int) Math.round(edge.x + (y - edge.yMin) * edge.slope);
                    if (x >= X_MIN && x <= X_MAX) {
                        intersections.add(x);
                    }
                }
            }

            // 排序并绘制有效线段
            Collections.sort(intersections);
            for (int i = 0; i < intersections.size(); i += 2) {
                if (i + 1 >= intersections.size()) break;
                int xStart = Math.max(intersections.get(i), X_MIN);
                int xEnd = Math.min(intersections.get(i + 1), X_MAX);
                if (xStart < xEnd) {
                    gc.strokeLine(xStart, y, xEnd, y);
                }
            }
        }
    }

    // 辅助类
    private static class Point {
        final int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
    }

    private static class Edge {
        final int yMin, yMax;
        double x, slope;

        Edge(int x1, int y1, int x2, int y2) {
            if (y1 < y2) {
                this.yMin = y1;
                this.yMax = y2;
                this.x = x1;
                this.slope = (x2 - x1) / (double)(y2 - y1);
            } else {
                this.yMin = y2;
                this.yMax = y1;
                this.x = x2;
                this.slope = (x1 - x2) / (double)(y1 - y2);
            }
        }
    }

    // 完整DDA画线算法
    private void drawLine(GraphicsContext gc, int x1, int y1, int x2, int y2, Color color) {
        gc.setStroke(color);
        int dx = x2 - x1;
        int dy = y2 - y1;
        int steps = Math.max(Math.abs(dx), Math.abs(dy));

        float xInc = dx / (float)steps;
        float yInc = dy / (float)steps;

        float x = x1;
        float y = y1;
        for (int i = 0; i <= steps; i++) {
            gc.strokeOval(Math.round(x), Math.round(y), 1, 1);
            x += xInc;
            y += yInc;
        }
    }

    // 完整虚线绘制方法
    private void drawDashedRectangle(GraphicsContext gc, int x, int y, int width, int height, Color color) {
        gc.setStroke(color);
        gc.setLineDashes(5);
        drawLine(gc, x, y, x + width, y, color);
        drawLine(gc, x + width, y, x + width, y + height, color);
        drawLine(gc, x + width, y + height, x, y + height, color);
        drawLine(gc, x, y + height, x, y, color);
        gc.setLineDashes(null);
    }

    public static void main(String[] args) {
        launch(args);
    }
}